<?php
namespace zake\foundation;

use zake\helper\Console;
use zake\helper\Grant;
use zake\helper\Session;
use zake\foundation\able\BeeAble;
use zake\foundation\able\RunAble;
use zake\foundation\able\ProcessAble;
use zake\foundation\able\stream\DuplexAble;
use zake\foundation\reactor\trigger\Channel;
use zake\foundation\able\reactor\ChannelAble;


/**
 * 进程管理
 *
 * Class Process
 * @package zake\foundation
 *
 * @author liu.bin 2020-03-31 10:01
 */
class Process implements ProcessAble
{
    /**
     *
     * @var null|RunAble
     */
    private $runnable = null;

    /**
     * 子进程pid
     * @var int
     */
    private $pid = 0;

    /**
     * @var null|ChannelAble|BeeAble
     */
    private $channel = null;


    /**
     * process hash
     * @var string
     */
    private $processId = 0;


    /**
     * 是否守护
     * @var bool
     */
    private static $_daemonize = false;


    /**
     * 构造器
     * 创建子进程
     *
     * @param \Closure|RunAble $callback
     * @param DuplexAble|Channel|null $channel
     * @return ProcessAble|null
     *
     * @author liu.bin 2020-03-19 15:30
     */
    public static function create($callback,$channel=null)
    {
        $processId  = Grant::permitProcessId();
        if(empty($processId)){
            Console::error("create child process fail. because cannot get the process id");
            return null;
        }
        if($callback instanceof RunAble || is_callable($callback)){
            $process = new static($processId,$callback,$channel);
            return $process;
        }
        return null;
    }


    /**
     * 构造器
     *
     * @param int $processId
     * @param \Closure|RunAble $callback
     * @param Channel $channel
     * Process constructor.
     */
    private function __construct($processId,$callback,$channel=null)
    {
        $this->processId    = $processId;
        $this->runnable     = $callback;
        $this->channel      = $channel;
    }


    /**
     * 创建子进程
     *
     * @author liu.bin 2020-03-25 21:44
     */
    public function start()
    {
        $pid = Process::fork();
        if(0 === $pid){
            if(!is_null($this->channel)){
                // 初始化 channel
                $clusterId = 1;
                $processId = Process::pid();
                $this->channel->init($processId);
                $connectId = Grant::permitConnectId();
                $this->channel->setConnectId($connectId);
                $this->channel->setSessionId(Session::id($clusterId,$processId,$this->channel->getConnectId()));
            }
            if($this->runnable instanceof RunAble){
                $this->runnable->run($this->processId,$this->channel);
            }elseif(is_callable($this->runnable)){
                call_user_func_array($this->runnable,[$this->processId,$this->channel]);
            }
            exit;
        }

        if($pid > 0){
            // 父进程
            $clusterId = 1;
            $processId = Process::pid();
            $connectId = Grant::permitConnectId();
            $this->channel->init(Process::pid());
            $this->channel->setConnectId($connectId);
            $this->channel->setSessionId(Session::id($clusterId,$processId,$this->channel->getConnectId()));
            $this->pid = $pid;
        }
    }


    /**
     * 子进程pid
     *
     * @author liu.bin 2020-03-25 21:51
     */
    public function getPid()
    {
        return $this->pid;
    }


    /**
     * 子进程编号
     *
     * @author liu.bin 2020-03-31 09:49
     */
    public function getProcessId()
    {
        return $this->processId;
    }


    /**
     *
     * @return int
     *
     * @author liu.bin 2020-04-14 18:38
     */
    public static function fork()
    {
        $pid = pcntl_fork();
        if($pid < 0){
            Console::error("create process fail.");
            exit;
        }
        return $pid;
    }


    /**
     * 进程id
     * @return int
     *
     * @author liu.bin 2020-03-31 09:50
     */
    public static function pid()
    {
        return posix_getpid();
    }



    /**
     * 阻塞等待子进程退出返回
     *
     * @author liu.bin 2020-03-25 21:45
     */
    public static function wait()
    {
        pcntl_wait($status);
        return $status;
    }


    /**
     * 守护进程
     *
     * @author liu.bin 2020-04-14 15:41
     */
    public static function daemonize($flag=true)
    {
        if(empty($flag)){
            return false;
        }
        // 改变工作目录
        umask(0);
        // 1：创建子进程
        if(Process::fork() > 0){
            // 主进程
            //Console::success("master-1 exit success");
            exit;
        }

        // 2：子进程创建新会话
        if(!posix_setsid() ){
            Console::error("master exit success");
            exit;
        }

        // 创建子进程
        if( Process::fork() > 0 ) {
            // 主进程
            //Console::success("master-2 exit success");
            exit;
        }

        chdir( '/tmp' );
        fclose(STDIN);
        fclose(STDOUT);
        fclose(STDERR);
        static::$_daemonize = true;
        return true;
    }


    /**
     * 是否守护
     * @return bool
     *
     * @author liu.bin 2020-04-14 18:46
     */
    public static function isDaemonize()
    {
        return static::$_daemonize;
    }

}





